package com.example.LightningController;

import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.os.Bundle;
import android.util.Log;
import android.view.*;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;

import java.util.Set;

/**
 * Created with IntelliJ IDEA.
 * User: master
 * Date: 5/11/13
 * Time: 12:34 PM
 * To change this template use File | Settings | File Templates.
 */
public class ControllerActivity extends Activity {
    private float[] statePos = new float[2];
    private float[] movementPos = new float[2];
    private float angle = 0f;
        private Zone selectedZone;
    private float storedAngle2 = 0f;
    private float storedAngle3 = 0f;
    private SPPManager sppInterfaceManager;
    private BluetoothDevice selectedDevice = null;

    private static Bitmap imageOriginal, imageScaled;
    private static Matrix matrix;

    private ImageView controllerImage;
    private int dialerHeight, dialerWidth;

    private GestureDetector detector;

    // needed for detecting the inversed rotations
    private boolean[] quadrantTouched;

    private boolean allowRotating;
    OrientationEventListener myOrientationEventListener;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.controller_activity);

        this.sppInterfaceManager = new SPPManager(this, null);

        final Bundle bundle = getIntent().getExtras();
        if (bundle != null) {
            if (bundle.containsKey("selected_zone")) {
                if (bundle.get("selected_zone") instanceof Zone) {
                    Utility.showProgressDialog(ControllerActivity.this, "Connecting to device");

                    this.selectedZone = ((Zone) bundle.get("selected_zone"));

                    Set<BluetoothDevice> foundDevices = ControllerActivity.this.sppInterfaceManager.searchForDevices();
                    if (foundDevices != null) {
                        for (BluetoothDevice aDevice : foundDevices) {
                            if (aDevice.getAddress().equals(this.selectedZone.getMacAddress())) {
//                                Utility.showProgressDialog(ControllerActivity.this, "Connecting to device");
                                ControllerActivity.this.selectedDevice = aDevice;
                                Utility.changeCurrentProgressDialogText("Retrieving information");
                                ControllerActivity.this.sppInterfaceManager.pairDevice(aDevice, SPPManager.AccessType.OPEN_EXISTING_DEVICE);
                                ControllerActivity.this.sppInterfaceManager.setConnectionEstablishedListener(new OnConnectionEstablishedListener() {
                                    @Override
                                    public void onConnectionEstablished() {
                                        getIntent().removeExtra("selected_zone");
                                        getIntent().putExtra("selected_zone_memory", ControllerActivity.this.selectedZone);
                                        Utility.stopProgressDialog();
                                        ControllerActivity.this.sppInterfaceManager.setNewMessageListener(new OnNewMessageListener() {
                                            @Override
                                            public void onMessageListener(final String newMessage) {
                                                ControllerActivity.this.runOnUiThread(new Runnable() {
                                                    @Override
                                                    public void run() {
                                                        if (!newMessage.equals("") && !newMessage.equals("ERTEMPERATURE!!!!")) {
                                                            ((ProgressBar) findViewById(R.id.temperature_progress)).setVisibility(View.INVISIBLE);
                                                            ((ProgressBar) findViewById(R.id.power_progress)).setVisibility(View.INVISIBLE);
                                                            ((TextView) findViewById(R.id.temperature_text)).setVisibility(View.VISIBLE);
                                                            ((TextView) findViewById(R.id.power_text)).setVisibility(View.VISIBLE);
                                                            ((TextView) findViewById(R.id.temperature_text)).setText(newMessage);
                                                        }
                                                    }
                                                });
                                            }
                                        });
                                    }
                                });
//                                Utility.stopProgressDialog();
                            }
                        }
                    }
                }
            } else if (bundle.containsKey("selected_zone_memory")){
                ControllerActivity.this.selectedZone = (Zone)bundle.get("selected_zone_memory");
                //rotateDialer(getIntent().getIntExtra("current_angle", 0));
            }
        }

        if (this.selectedZone != null)
            ((TextView) findViewById(R.id.selected_zones_header)).setText(this.selectedZone.getTag());

        // load the image only once
        if (imageOriginal == null) {
            imageOriginal = BitmapFactory.decodeResource(getResources(), R.drawable.controller);
        }

        // initialize the matrix only once
        if (matrix == null) {
            matrix = new Matrix();
        } else {
            // not needed, you can also post the matrix immediately to restore the old state
            matrix.reset();
        }

        detector = new GestureDetector(this, new MyGestureDetector());

        // there is no 0th quadrant, to keep it simple the first value gets ignored
        quadrantTouched = new boolean[]{false, false, false, false, false};

        allowRotating = true;

        controllerImage = (ImageView) findViewById(R.id.controller_image);
        controllerImage.setOnTouchListener(new MyOnTouchListener());
        controllerImage.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

            @Override
            public void onGlobalLayout() {
                // method called more than once, but the values only need to be initialized one time

                if (dialerHeight == 0 || dialerWidth == 0) {
                    dialerHeight = controllerImage.getHeight();
                    dialerWidth = controllerImage.getWidth();

                    // resize
                    Matrix resize = new Matrix();
                    resize.postScale((float) Math.min(dialerWidth, dialerHeight) / (float) imageOriginal.getWidth(), (float) Math.min(dialerWidth, dialerHeight) / (float) imageOriginal.getHeight());
                    imageScaled = Bitmap.createBitmap(imageOriginal, 0, 0, imageOriginal.getWidth(), imageOriginal.getHeight(), resize, false);

                    // translate to the image view's center
                    float translateX = dialerWidth / 2 - imageScaled.getWidth() / 2;
                    float translateY = dialerHeight / 2 - imageScaled.getHeight() / 2;
                    matrix.postTranslate(translateX, translateY);

                    controllerImage.setImageBitmap(imageScaled);
                    controllerImage.setImageMatrix(matrix);
                }
            }
        });
    }

    private void rotateDialer(float degrees) {
        matrix.postRotate(degrees, dialerWidth / 2, dialerHeight / 2);

        controllerImage.setImageMatrix(matrix);

//        sendAsyncBroadcastToBTDevice(degrees);
    }

    private void sendAsyncBroadcastToBTDevice(String degrees) {
        //To change body of created methods use File | Settings | File Templates.
        ControllerActivity.this.sppInterfaceManager.transmitToDevice(degrees);
    }

    private double getAngle(double xTouch, double yTouch) {
        double x = xTouch - (dialerWidth / 2d);
        double y = dialerHeight - yTouch - (dialerHeight / 2d);

        switch (getQuadrant(x, y)) {
            case 1:
                return Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;

            case 2:
            case 3:
                return 180 - (Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);

            case 4:
                return 360 + Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;

            default:
                // ignore, does not happen
                return 0;
        }
    }

    private static int getQuadrant(double x, double y) {
        if (x >= 0) {
            return y >= 0 ? 1 : 4;
        } else {
            return y >= 0 ? 2 : 3;
        }
    }

    public void cancelControl(View v) {
        finish();
    }

    @Override
    protected void onPause() {
        super.onPause();

//        if (!orientationChanged) {
//            this.sppInterfaceManager.closeDeviceConnection();
//            finish();
//        }
    }

    private class MyOnTouchListener implements View.OnTouchListener {

        private double startAngle;

        @Override
        public boolean onTouch(View v, MotionEvent event) {

            switch (event.getAction()) {

                case MotionEvent.ACTION_DOWN:

                    // reset the touched quadrants
                    for (int i = 0; i < quadrantTouched.length; i++) {
                        quadrantTouched[i] = false;
                    }

                    allowRotating = false;

                    startAngle = getAngle(event.getX(), event.getY());
                    break;

                case MotionEvent.ACTION_MOVE:
                    if ((getAngle(event.getX(), event.getY()) >= 0 && getAngle(event.getX(), event.getY()) <= 220) || (getAngle(event.getX(), event.getY()) >= 320 && getAngle(event.getX(), event.getY()) <= 355)) {

                        if (getAngle(event.getX(), event.getY()) >= 0 && getAngle(event.getX(), event.getY()) <= 220) {
                            Log.i("sending", "" + (Math.round((220 - Math.round(getAngle(event.getX(), event.getY()))) / 3.72) + 30));
                            sendAsyncBroadcastToBTDevice(String.valueOf(Math.round((220 - Math.round(getAngle(event.getX(), event.getY()))) / 3.72) + 30));
                        }
                        if (getAngle(event.getX(), event.getY()) >= 320 && getAngle(event.getX(), event.getY()) <= 355) {
                            Log.i("sending", "" + (Math.round((355 - Math.round(getAngle(event.getX(), event.getY()))) / 2.5) + 88));
                            sendAsyncBroadcastToBTDevice(String.valueOf(Math.round((355 - Math.round(getAngle(event.getX(), event.getY()))) / 2.5) + 88));
                        }

                        getIntent().putExtra("current_angle", getAngle(event.getX(), event.getY()));

                        double currentAngle = getAngle(event.getX(), event.getY());
                        Log.i("controller", "" + currentAngle);
                        rotateDialer((float) (startAngle - currentAngle));
                        startAngle = currentAngle;
                    }
                    break;

                case MotionEvent.ACTION_UP:
                    allowRotating = false;
                    break;
            }

            // set the touched quadrant to true
            quadrantTouched[getQuadrant(event.getX() - (dialerWidth / 2), dialerHeight - event.getY() - (dialerHeight / 2))] = true;

            detector.onTouchEvent(event);

            return true;
        }
    }

    private class MyGestureDetector extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

            // get the quadrant of the start and the end of the fling
            int q1 = getQuadrant(e1.getX() - (dialerWidth / 2), dialerHeight - e1.getY() - (dialerHeight / 2));
            int q2 = getQuadrant(e2.getX() - (dialerWidth / 2), dialerHeight - e2.getY() - (dialerHeight / 2));

            // the inversed rotations
            if ((q1 == 2 && q2 == 2 && Math.abs(velocityX) < Math.abs(velocityY))
                    || (q1 == 3 && q2 == 3)
                    || (q1 == 1 && q2 == 3)
                    || (q1 == 4 && q2 == 4 && Math.abs(velocityX) > Math.abs(velocityY))
                    || ((q1 == 2 && q2 == 3) || (q1 == 3 && q2 == 2))
                    || ((q1 == 3 && q2 == 4) || (q1 == 4 && q2 == 3))
                    || (q1 == 2 && q2 == 4 && quadrantTouched[3])
                    || (q1 == 4 && q2 == 2 && quadrantTouched[3])) {

                controllerImage.post(new FlingRunnable(-1 * (velocityX + velocityY)));
            } else {
                // the normal rotation
                controllerImage.post(new FlingRunnable(velocityX + velocityY));
            }

            return true;
        }
    }

    private class FlingRunnable implements Runnable {

        private float velocity;

        public FlingRunnable(float velocity) {
            this.velocity = velocity;
        }

        @Override
        public void run() {
            if (Math.abs(velocity) > 5 && allowRotating) {
                rotateDialer(velocity / 75);
                velocity /= 1.0666F;

                // post this instance again
                controllerImage.post(this);
            }
        }
    }

    @Override
    public void onBackPressed() {
        this.sppInterfaceManager.closeDeviceConnection();
        finish();
    }
}
